package cn.edu.nju.ws.sview.views.data;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Properties;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
import java.util.Random;

import cn.edu.nju.ws.sview.cache.URIIDCache;
import cn.edu.nju.ws.sview.cache.URILabelCache;
import cn.edu.nju.ws.sview.cache.URITypeCache;
import cn.edu.nju.ws.sview.database.DBConnectionFactory;
import cn.edu.nju.ws.sview.util.URIUtil;
import cn.edu.nju.ws.sview.views.Entity;
import cn.edu.nju.ws.sview.views.data.EntityBrowsing.QuadItem;

/**
 * 用于载入conf.properties中特定常量的类
 * @author lxq
 *
 */
class GetConfProperties{
	static String getPropertyValue(String property){
		Properties prop = new Properties();
		try {
			prop.load(DBConnectionFactory.class.getClassLoader().getResourceAsStream("conf.properties"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return prop.getProperty(property);
	}
}

class PackingService{
	static QuadItem pack(int itemId, int itemType, String lang){
		if(itemType == 2){//原始URI
			try {
				HashSet<Integer> type_ids = URITypeCache.getInstance().getTypes(itemId);
				ArrayList<Entry<Integer, String>> types = new ArrayList<Entry<Integer, String>>();
				Iterator<Integer> iter = type_ids.iterator();
				while(iter.hasNext()){
					Integer i = iter.next();
					String typeLabel = URILabelCache.getInstance().getLabel(i, lang);
					if(typeLabel.equals(""))
						typeLabel = URIUtil.getLocalname(URIIDCache.getInstance().getURI(i));
					types.add(new SimpleEntry(i, typeLabel));
				}
				String name = URILabelCache.getInstance().getLabel(itemId, lang);
				if( name.equals("")){
					String uri = URIIDCache.getInstance().getURI(itemId);
					name = URIUtil.getLocalname(uri);
				}
				return new QuadItem(2, itemId, name, types, lang);
			} catch (Throwable e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		else{
			ArrayList<Entry<Integer, String>> types =
					Entity.getClassTypesById(itemId,lang);
			String name = Entity.getNameByEntityID(itemId, lang);
			return new QuadItem(itemType, itemId, name, types, 
					lang);
		}
		return null;
	}
}

enum RecType {GENERAL_HISTORY_BASED, CUST_ENTITY_RELATION_BASED, COLLECTION_RELATION_BASED, URI_RELATION_BASED}

/**
 * 推荐源的基类
 * @author lxq
 *
 */
abstract class RecommandSrc{
	protected String lang; 
	protected RecType recType;
	
	/**
	 * 
	 * @param t RecType
	 * @param l 当前环境使用的语言
	 */
	protected RecommandSrc(RecType t, String l){
		lang = l;
		recType = t;
	}
		
	public abstract LinkedList<QuadItem> getRecommandItems(int itemId, int itemType, int requiredNum) throws Exception;
	
	/**
	 * 将推荐得到的item的id和type得到对应实体的信息，封装成要求的带着推荐理由的QuadItemWithRecCause。
	 * @param itemId
	 * @param itemType
	 * @return 封装好的QuadItemWithRecCause
	 */
	protected QuadItem pack(RecommItem item){
		return PackingService.pack
				(item.getItemId(), item.getItemType(), this.lang);
	}
}

public class GenHistRecomm extends RecommandSrc{
	private RecommCache midItems;
	public GenHistRecomm(String l){
		
		super(RecType.GENERAL_HISTORY_BASED, l);
	}
	
	public LinkedList<QuadItem> getRecommandItems(int itemId, int itemType, int requiredNum) throws SQLException{
		
		//从DB的rec_relation表中取出今天之前的中间结果
		Date visitTime = new Date();
		midItems = RecommCache.getInstance(itemId, itemType, visitTime);
		SimpleDateFormat m = new SimpleDateFormat("yyyy-MM-dd");
		
		String backStartTime = "'" + m.format(visitTime) + " 00:00:00'";
		
		String typeRestrict = "";
		//如果当前的item为1，2，则可以推荐类型为1、2的items。如果是dataset（值为uri），则限定推荐的类型为uri.如果是3的话，只推荐3.
		if(itemType == 1 || itemType == 2)
			typeRestrict = " item_type in (1, 2) ";
		else if(itemType == 11 || itemType == 12)
			typeRestrict = " item_type in (11, 12) ";
		else
			typeRestrict = " item_type = " + itemType + " ";
		
		//Entry为<user_id, visit_time>
		ArrayList<Entry<Integer, String>> relatedUsers = new ArrayList<Entry<Integer, String>>();
		
		//第一次查询数据库，得到特定时间段内有哪些用户访问了这个实体
		Connection conn = DBConnectionFactory.getConnection();
		try{
			PreparedStatement ps = conn.
    	        prepareStatement("SELECT user_id, visit_time FROM visit_logs WHERE " +
    	            		"view_id = -1   AND item_id = ? AND item_type = ? AND visit_time >" 
    	        		+ backStartTime + " AND event_type = 'visit';");
			ps.setInt(1, itemId);
			ps.setInt(2, itemType);
			
			ResultSet rs = ps.executeQuery();
			try{
				while(rs.next()){
					int uId = rs.getInt("user_id");
					String vTime = rs.getDate("visit_time").toString() + " " + rs.getTime("visit_time").toString();
					relatedUsers.add(new SimpleEntry<Integer, String>(uId, vTime));
				}
			}finally{
				rs.close();     
				ps.close();
			}
		
			//第二次查询，对于每一个用户的每次访问，查询最近10min中该用户访问的该语言环境下的item，加入到中间结果中
			for(Entry<Integer, String> e : relatedUsers){
				try{
					int uId = e.getKey().intValue();
					String vTime = e.getValue();
					String query = "SELECT DISTINCT item_id, item_type FROM visit_logs WHERE user_id = " +
						"? AND lang = ? AND view_id = -1 AND (visit_time BETWEEN " +
						"? AND DATE_ADD(?, INTERVAL ? MINUTE)) AND event_type = 'visit' AND "
						+ typeRestrict + ";";
					
					
					ps = conn.prepareStatement(query);
					ps.setInt(1, uId);
					ps.setString(2, this.lang);
					ps.setString(3, vTime);
					ps.setString(4, vTime);
					ps.setInt(5, Consts.VIEW_TIME_SPAN);
					rs = ps.executeQuery();
					while(rs.next()){
						int iId = rs.getInt("item_id");
						int iType = rs.getInt("item_type");
						RecommItem temp = new RecommItem(iId, iType);
						if(iId != itemId){
							midItems.union(temp, visitTime);
						}
					}
				}finally{
					ps.close();
					rs.close();
				}
			}
		}finally{
			conn.close();
		}
		
		PriorityQueue<RecommItem> pq = midItems.getPriorityQueue();
		
		LinkedList<QuadItem> l = new LinkedList<QuadItem>();
		RecommItem temp;
		while((temp = pq.poll()) != null){
			l.add(PackingService.pack(temp.getItemId(), temp.getItemType(), this.lang));
		}
		return l;
	}
	
	
	
	 static void randomVisitGenerator(int num, Date tillTime) throws SQLException{
		//随机插入
		SimpleDateFormat m=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String timeS = m.format(tillTime);
		Connection conn = DBConnectionFactory.getConnection();
		try{
			PreparedStatement ps = conn.prepareStatement("DELETE FROM visit_logs");
			ps.executeUpdate();
			ps.close();
			ArrayList<Integer> l = new ArrayList<Integer>();
			ps = conn.prepareStatement("SELECT id FROM identity");
			ResultSet rs = ps.executeQuery();
			while(rs.next()){
			l.add(rs.getInt("id"));
		}
		ps.close();
		rs.close();
		
		conn.setAutoCommit(false);
		for(int i = 0; i < num; i++){
			Random rand = new Random();
			String datetime = "DATE_SUB('" + timeS + "', INTERVAL " + rand.nextInt(2880) + " MINUTE)";
			ps = conn.prepareStatement("INSERT INTO visit_logs " +
				"(user_id, event_type, item_type, item_id, view_id, visit_time, lang) VALUES(?, 'visit', '1', ?, -1, " + datetime + ", 'en');");
			
			ps.setInt(1, rand.nextInt(10));
			ps.setInt(2, l.get(rand.nextInt(l.size())));
			ps.executeUpdate();
		}
		conn.commit();
		conn.setAutoCommit(true);
		ps.close();
		}finally{
			conn.close();
		}
	}
	
}
